package org.folio.rest.impl;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author lee
 * @Classname ParallelAsyncTaskExecutor
 * @Description TODO
 * @Date 2020/8/12 17:02
 * @Created by lee
 */
public class ParallelAsyncTaskExecutor implements AsyncResult<Object> {
    private final Map<Handler<Future<Object>>, Future<Object>> tasks;
    private List<Object> result;
    private Handler<AsyncResult<Object>> handler;

    public ParallelAsyncTaskExecutor(Map<Handler<Future<Object>>, Future<Object>> tasks) {
        this.tasks = tasks;
    }

    public ParallelAsyncTaskExecutor() {
        this.tasks = new HashMap<>();
    }

    private void checkHandler() {
        if (isComplete()) {
            callHandler();
        }
    }

    private void callHandler() {
        if (handler != null) {
            handler.handle(this);
            handler = null;
        }
    }

    private boolean isComplete() {
        if (tasks.isEmpty()) {
            return false;
        }
        return tasks.values().stream().allMatch(f -> {
            return f.isComplete();
        });
    }

    public void addAsyncTask(Handler<Future<Object>> handler) {
        Future<Object> future = Future.future();
        future.setHandler(f -> {
            checkHandler();
        });
        this.tasks.put(handler, future);
    }

    public void start() {
        if (this.tasks.isEmpty()) {
            callHandler();
            return;
        }
        this.tasks.forEach((handler, future) -> {
            try {
                handler.handle(future);
            } catch (Throwable e) {
                future.tryFail(e);
            }
        });
    }
   public void setHandler(Handler<AsyncResult<Object>> handler){
        this.handler = handler;
        checkHandler();
   }
    @Override
    public Object result() {
        List<Object> result = new ArrayList<>();
        tasks.forEach((consumer,future)->{
           result.add(future.result());
      });
      return result;
    }

    @Override
    public Throwable cause() {
         if(failed()){
    Exception e = new Exception();
    tasks.forEach((c,f)->{
      if(f.failed()){
          if(f.failed()){
              if(e.getCause() == null){
                  e.initCause(f.cause());
              }else{
                  e.addSuppressed(f.cause());
              }
          }
      }

    });
    return e;
        }
         return null;
    }

    @Override
    public boolean succeeded() {
        return tasks.values().stream().allMatch(f ->f.succeeded());
    }

    @Override
    public boolean failed() {
        return tasks.values().stream().allMatch(f ->f.failed());
    }
}
